﻿// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. 
// See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading.Actors;
using System.Diagnostics.Tracing;
using System.Diagnostics.Contracts;

namespace ActorAnalyzer
{
    public partial class ActorTabPageControl : UserControl
    {
        internal TreeNodeCollection m_eventRoot;
        private FabricActorClient m_client;
        private IDisposable m_subscriptionDisposable;

        public ActorTabPageControl(string fabricConnectionString, string actorUri, string partitionName, bool useGateway)
        {
            Contract.Requires(fabricConnectionString != null);

            InitializeComponent();

            m_eventRoot = treeView1.Nodes;
            var isLocal = fabricConnectionString.Contains("localhost") || fabricConnectionString.Contains("127");
            InitializeActorClientAsync(fabricConnectionString, actorUri, partitionName, useGateway);
        }

        private async void InitializeActorClientAsync(string fabricConnectionString, string actorUri, string partitionName, bool useGateway)
        {
            // Launch these in the background
            try
            {
                await Task.Factory.StartNew(() =>
                {
                    m_client = new FabricActorClient(
                        new Uri("net.tcp://" + fabricConnectionString),
                        new Uri(actorUri),
                        useGateway,
                        partitionName);

                    var observer = new ETWEventObserver(this, m_eventRoot);
					// Support filtering events based on type name here.
                    // Hack: Hook this up to some part of the UI sometime.  But this shows how it can be done.
                    //observer.OmitEventTypes(new String[] { "ActorMethodSignatureFailure" });

                    m_subscriptionDisposable = m_client.Subscribe("ETW", observer);

                    if (MainForm.ETWEventLevel.HasValue)
                    {
                        ChangeEventLevel(MainForm.ETWEventLevel.Value);
                    }
                });
            }
            catch (Exception exc)
            {
                Exception inner = exc.InnerException ?? exc;
                MessageBox.Show(String.Format("Exception from FAC construct/subscribe.  {0}: {1}", inner.GetType(), inner.Message), 
                    "Error connecting to actor");
            }

        }

        public void ChangeEventLevel(EventLevel level)
        {
            if (m_client == null)
            {
                MessageBox.Show("The current tab in Actor Analyzer isn't connected to a cluster, at least not yet.");
                return;
            }

            String configString = "EventLevel=" + level.ToString();
            m_client.Configure(configString);
        }

        public void Unsubscribe()
        {
            if (m_subscriptionDisposable != null)
            {
                m_subscriptionDisposable.Dispose();
                m_subscriptionDisposable = null;
            }
        }
    }

    internal class ETWEventObserver : IObserver<ActorEvent>
    {
        private TreeNodeCollection m_eventRoot;
        private Control m_parent;
        private String[] m_omitEventTypes;

        public ETWEventObserver(Control parent, TreeNodeCollection root)
        {
            m_parent = parent;
            m_eventRoot = root;
        }

        public void OmitEventTypes(String[] eventTypesToOmit)
        {
            m_omitEventTypes = eventTypesToOmit;
        }

        public void OnCompleted()
        {
        }

        public void OnError(Exception error)
        {
            throw new NotImplementedException();
        }

        public void OnNext(ActorEvent value)
        {
            try
            {
                var convertedArgs = ActorETWEventInfo.FromJson(value.EventPayload);
                var eventTitle = ActorETWEventSource.GetTitle(convertedArgs);

                if (m_omitEventTypes != null)
                {
                    String eventType = eventTitle.Substring(eventTitle.LastIndexOf(':') + 2);
                    if (m_omitEventTypes.Contains(eventType))
                        return;
                }

                var eventDetails = ActorETWEventSource.GetDetails(convertedArgs);

                var eventFgColor = Color.Black;
                var eventBgColor = Color.White;
                switch (convertedArgs.Level)
                {
                    case EventLevel.Warning: eventFgColor = Color.Fuchsia; break;
                    case EventLevel.Error: eventFgColor = Color.Red; break;
                    case EventLevel.Critical: eventFgColor = Color.Red; eventBgColor = Color.Yellow; break;
                }

                TreeNode newNode = new TreeNode(eventTitle);
                newNode.ForeColor = eventFgColor;
                newNode.BackColor = eventBgColor;
                if (eventDetails != null)
                {
                    foreach (var detail in eventDetails)
                    {
                        TreeNode detailNode = new TreeNode(detail);
                        newNode.Nodes.Add(detailNode);
                    }
                }

                m_parent.Invoke((Action)(() =>
                {
                    m_eventRoot.Add(newNode);
                }));
            }
            catch(Exception e)
            {
                String message = String.Format("The Actor Analyzer threw an exception trying to handle an event from the actor.\r\n" +
                                  "  {0}: {1}\r\n" +
                                  "{2}", e.GetType().Name, e.Message, e.StackTrace);
                MessageBox.Show(message, "Actor Analyzer failed to handle an event correctly");
            }
        }
    }

}
